/*****************************************************************************
 * Copyright (C) Stacy Curl. All rights reserved.                            *
 * ------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the BSD      *
 * style license a copy of which has been included with this distribution in *
 * the LICENSE.txt file.                                                     *
 *****************************************************************************/
package picounit.features.coverage_measuring.mutator;

import picounit.features.coverage_measuring.SuiteDecorator;

import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import junit.framework.TestCase;
import junit.framework.TestSuite;

public class CoverageRegistry implements IdSource, CoverageListener, SuiteDecorator {
	private final Map<Integer, CoveragePoint> pointsToCover = new HashMap<Integer, CoveragePoint>();
	private final Set<CoveragePoint> pointsCovered = new HashSet<CoveragePoint>();

	private final CoverageState coverageState;

	private final Comparator<String> coverageComparator = new Comparator<String>() {
		public int compare(String left, String right) {
			long leftPercentage = percentageCovered(left);
			long rightPercentage = percentageCovered(right);

			if (leftPercentage == rightPercentage) {
				return Integer.valueOf(coveragePoints(right))
					.compareTo(coveragePoints(left));
			}

			return Long.valueOf(rightPercentage).compareTo(
				leftPercentage);
		}
	};

	private int currentId = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////
	// IdSource methods
	///////////////////////////////////////////////////////////////////////////////////////////////

	public CoverageRegistry(CoverageState coverageState) {
		this.coverageState = coverageState;
	}

	public int nextId(String className, String methodName, int lineNumber) {
		pointsToCover.put(currentId, new CoveragePoint(className, methodName, lineNumber));

		return currentId++;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////
	// CoverageListener methods
	///////////////////////////////////////////////////////////////////////////////////////////////

	public void covered(int coverageId) {
		pointsCovered.add(pointsToCover.get(coverageId));
//		System.out.println("Covered: [" + coverageId + "] = " + pointsToCover.get(coverageId));
	}

	///////////////////////////////////////////////////////////////////////////////////////////////
	// SuiteDecorator methods
	///////////////////////////////////////////////////////////////////////////////////////////////

	public TestSuite decorate(TestSuite testSuite) {
		TestSuite coverageSuite = coverageTestSuite();

		if (coverageState.isCoverageEnabled() && coverageSuite.countTestCases() > 0) {
			testSuite.addTest(coverageSuite);
		}

		return testSuite;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////
	// Implementation
	///////////////////////////////////////////////////////////////////////////////////////////////

	private TestSuite coverageTestSuite() {
		TestSuite coverageSuite = new TestSuite("Coverage Results");

		for (String className : classesCovered().sort(coverageComparator)) {
			coverageSuite.addTest(coverageTestCase(className));
		}

		return coverageSuite;
	}

	private TestCase coverageTestCase(String className) {
		return new CoverageTestCase(coverageSummary(className), className);
	}

	private SortableSet<String> classesCovered() {
		SortableSet<String> classesToCover = new SortableHashSet<String>();

		for (CoveragePoint coveragePoint : pointsToCover.values()) {
			classesToCover.add(coveragePoint.className());
		}

		return classesToCover;
	}

	private String coverageSummary(String className) {
		return "[" + coveredPoints(className) + "/" + coveragePoints(className) + " = " +
			percentageCovered(className) + "%]";
	}

	private long percentageCovered(String className) {
		return percentage(coveredPoints(className), coveragePoints(className));
	}

	private long percentage(double numerator, double denominator) {
		return Math.round((numerator / denominator) * 100);
	}

	private int coveragePoints(String className) {
		return numberOfMatchingCoveragePoints(pointsToCover.values(), className);
	}

	private int coveredPoints(String className) {
		return numberOfMatchingCoveragePoints(pointsCovered, className);
	}

	private int numberOfMatchingCoveragePoints(Collection<CoveragePoint> coveragePoints, String className) {
		int count = 0;

		for (CoveragePoint coveragePoint : coveragePoints) {
			if (coveragePoint.hasClassName(className)) {
				count++;
			}
		}

		return count;
	}
}


